package com.cn.sl.server.api.system.user.service.impl

import com.cn.sl.server.api.system.menu.model.dto.MenuDto
import com.cn.sl.server.api.system.role.dao.RoleDao
import com.cn.sl.server.api.system.user.dao.rdb.AdminUserDao
import com.cn.sl.server.api.system.user.model.dto.AddAdminUserDto
import com.cn.sl.server.api.system.user.model.dto.AdminUserAppendRoleDto
import com.cn.sl.server.api.system.user.model.dto.AdminUserDto
import com.cn.sl.server.api.system.user.model.dto.AdminUserListQueryDto
import com.cn.sl.server.api.system.user.model.dto.EditAdminUserDto
import com.cn.sl.server.api.system.user.model.dto.UpdateAdminUserStatusDto
import com.cn.sl.server.api.system.user.model.dto.UserPhoneExistDto
import com.cn.sl.server.api.system.user.model.entity.AdminUserEntity
import com.cn.sl.server.api.system.user.model.entity.QAdminUserEntity
import com.cn.sl.server.api.system.user.service.AdminUserService
import com.cn.sl.server.api.system.user.util.encrypt
import com.cn.sl.server.common.annotation.transaction.Commit
import com.cn.sl.server.common.annotation.transaction.ReadOnly
import com.cn.sl.server.common.dao.BaseDao
import com.cn.sl.server.common.dto.PageDto
import com.cn.sl.server.extensions.ifNotNullOrBlank
import com.querydsl.core.BooleanBuilder
import org.springframework.data.domain.Page
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service

@Service
class AdminUserServiceImpl(
    val adminUserDao: AdminUserDao,
    val roleDao: RoleDao,
) : AdminUserService {

    @ReadOnly
    override fun findAll(dto: AdminUserListQueryDto): List<AdminUserDto> {
        return adminUserDao.findAll(
            query(dto),
            Sort.by(Sort.Order.desc(entity.id.metadata.name))
        )
            .map { AdminUserDto(it) }
    }

    @ReadOnly
    override fun find(dto: AdminUserListQueryDto): PageDto<AdminUserDto> {
        val page: Page<AdminUserEntity> = adminUserDao.findAll {
            val booleanBuilder = query(dto)

            BaseDao.PageQueryAct(
                booleanBuilder,
                dto.page,
                dto.pageSize,
                Sort.by(Sort.Direction.DESC, entity.id.metadata.name)
            )
        }
        return PageDto(
            dto.page,
            dto.pageSize,
            page.totalElements.toInt(),
            page.totalPages,
            page.content.map { AdminUserDto(it) }
        )
    }

    @ReadOnly
    override fun phoneExists(dto: UserPhoneExistDto): Boolean {
        return adminUserDao.findByPhoneAndIdNot(dto.phone, dto.id) != null
    }

    @ReadOnly
    override fun findMenu(id: Long): List<MenuDto> {
        return adminUserDao.findById(id).let {
            it.roles.map { it.menus }.flatten().map { MenuDto(it) }
        }
    }

    @Commit
    override fun add(dto: AddAdminUserDto) {
        val entity = dto.toEntity {
            roleDao.findAllById(it)
        }
        adminUserDao.save(entity)
    }

    @Commit
    override fun edit(dto: EditAdminUserDto) {
        adminUserDao.findById(dto.id).let {
            dto.merge(it) {
                roleDao.findAllById(it)
            }

            adminUserDao.save(it)
        }
    }

    @ReadOnly
    override fun findById(id: Long): AdminUserDto {
        return AdminUserDto(adminUserDao.findById(id))
    }

    @Commit
    override fun delete(id: Long) {
        adminUserDao.deleteById(id)
    }

    @Commit
    override fun updateStatus(dto: UpdateAdminUserStatusDto) {
        adminUserDao.findAllById(dto.ids).forEach {
            it.status = dto.status
            adminUserDao.save(it)
        }
    }

    @Commit
    override fun appendRole(dto: AdminUserAppendRoleDto) {
        val userEntities = adminUserDao.findAllById(dto.ids)
        val roleEntities = roleDao.findAllById(dto.roleIds)
        roleEntities.forEach { role ->
            val roleUserEntities = role.users.toMutableList()
            roleUserEntities.addAll(userEntities)
            role.users = roleUserEntities.distinctBy { it.id }
        }
        roleDao.saveAll(roleEntities)
    }

    @Commit
    override fun resetPassword(id: Long) {
        adminUserDao.findById(id).let {
            val (password, salt) = encrypt(it.phone)

            adminUserDao.updatePassword(it.id, password, salt)
        }
    }

    fun query(dto: AdminUserListQueryDto): BooleanBuilder {
        return BooleanBuilder().apply {
            dto.username.ifNotNullOrBlank {
                if (it.contains(",") || it.contains("，") || it.contains(" ")) {
                    val list = it.replace("，", ",").replace(" ", ",").split(",")
                    this.and(entity.username.`in`(list))
                } else {
                    this.and(entity.username.contains(it))
                }
            }
            dto.phone.ifNotNullOrBlank {
                this.and(entity.phone.contains(it))
            }
            dto.roleId?.let {
                this.and(entity.roles.any().id.eq(it))
            }
            dto.departmentId?.let {
                this.and(entity.departmentId.eq(it))
            }
        }
    }

    companion object {
        val entity: QAdminUserEntity = QAdminUserEntity.adminUserEntity
    }
}